package com.hspedu.qqserver.service;

import com.hspedu.qqcommon.Message;
import com.hspedu.qqcommon.MessageType;
import com.hspedu.qqcommon.User;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 吴宇~
 * @motto 我亦无他，唯手熟尔。
 * @edition jdk1.8
 * @reflection 0-1 喧闹任其喧闹，自由我自为之，我自风情万种，与世无争。
 * @date 2023/03/20/17:41
 */
@SuppressWarnings("all")
//这是服务端，在监听9999端口等待客户端的连接，并保持通讯
public class QQServer {
    private ServerSocket ss = null;
    //创建一个集合存放多个用户，如果是这些用户登录，就认为是合法的
//    private static HashMap<String ,User> validUser=new HashMap<>();//hashMap没有处理线程安全问题，

    //这里我们也可以使用ConcurrentHashMap处理并发的集合，没有线程安全问题
    private static ConcurrentHashMap<String, User> validUser = new ConcurrentHashMap<String, User>();

    //这个集合存放离线用户的id和离线时接收到的不同消息
    public static ConcurrentHashMap<String, Vector<Message>> offLineDB = new ConcurrentHashMap<>();

    /**
     * 使用安全处理线程的集合，登录时遍历是否以登录
     * private static ConcurrentHashMap<String ,User> signUser =new ConcurrentHashMap<String,User>();
     */
    private SendNewsToService sendNewsToService = new SendNewsToService();//使用这个属性推送新闻

    private SendOfflineMessage sendOfflineMessage = new SendOfflineMessage();

    static {
        /**
         * 静态代码块，进行类加载的时候会初始化一次
         */
        //如果用户传的id是此集合里面的,则是存在的用户
        validUser.put("100", new User("100", "123456"));
        validUser.put("200", new User("200", "123456"));
        validUser.put("300", new User("300", "123456"));
        validUser.put("至尊宝", new User("至尊宝", "123456"));
        validUser.put("紫霞仙子", new User("菩提老祖", "123456"));
        /**
         *把这儿删了！！把每一个userId都遍历
         */
        //定义一个不在线用户，这个用户永远不在线，
        Vector<Message> messages = new Vector<>();
        offLineDB.put("吴宇",messages);
//        offLineDB.put("100", new Vector<>());
//        offLineDB.put("200", new Vector<>());
//        offLineDB.put("300", new Vector<>());
//        offLineDB.put("至尊宝", new Vector<>());
//        offLineDB.put("紫霞仙子", new Vector<>());


    }

    //验证用户是否有效的方法，还应该解决不能多用户登录问题，
    private boolean checkUser(String userId, String passwd) {
        User user = validUser.get(userId);
        if (user == null) {//说明userId不存在，
            return false;
        }
        if (!(user.getPasswd().equals(passwd))) {//密码错误
            return false;
        }
        /**
         * 把所有已登录的取出来，逐个比较
         * Collection<User> values = signUser.values();
         *         for (User value : values) {
         *             if (value.getUserID().equals(userId)){
         *                 System.out.println(userId+"用户多点登录");
         *                 return false;
         *             }
         *         }
         */
        return true;
    }


    public QQServer() {
        //端口可以写在一个配置文件内
        try {
            //启动推送新闻的线程
            new Thread(sendNewsToService).start();
            System.out.println("服务端在9999端口监听。。。。");
            ss = new ServerSocket(9999);
            while (true) {//监听是循环的，当和某个客户端建立连接后是一直监听的
                Socket socket = ss.accept();//如果没有客户端连接，就会阻塞在这里，知道有下一个客户端连接
                //客户端首先发送的是账号密码，所以用输入流接收
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                User u = (User) ois.readObject();//读取客户端发送的对象，客户端应该是有一个数据库的，我们就使用集合凑合凑合就行了
                //创建一个Message对象准备回复客户端，
                /**
                 * 总之就是要返回一个message类型的类，这个类，这就搞不懂，就不能在客户端发送一个类过来，这个类使用Object接收，
                 * 结合之后，拿到数据库比对一下，对就返回一个true，不对就返回一个false，这不行?
                 * 把客户端和服务端的所有交流信息都封装在message里面
                 * u.getUserID().equals("100") && u.getPasswd().equals("123456")
                 */
                Message message = new Message();
                /**
                 * 这样可以验证到底数据有没有传进来
                 *System.out.println(u.getUserID()+" "+u.getPasswd());
                 */
                //验证：
                if (checkUser(u.getUserID(), u.getPasswd())) {

                    //一个用户登录，把这个用户从离线用户中移除,并比较是否有这个用户的离线消息，有的话发送
                    //合法用户
                    /**
                     * 把已登录客户加入到一个集合中
                     * signUser.put(u.getUserID(),u);
                     */

                    message.setMesType(MessageType.MESSAGE_LOGIN_SUCCEED);
                    //将message关联的对象回复给客户端
                    oos.writeObject(message);

                    //创建一个线程，和客户端保持通信，该线程需要持有Socket对象
                    ServerConnectClientThread serverConnectClientThread = new ServerConnectClientThread(socket, u.getUserID());
                    //启动该线程
                    serverConnectClientThread.start();
                    //把该线程对象放入到一个集合中，进行管理
                    MangerClientThreads.addClientThread(u.getUserID(), serverConnectClientThread);

                    /**
                     *
                     */
                    u.setSign(true);
                    sendOfflineMessage.SendOfflineMessage(u.getUserID());

                } else {//登录失败
                    System.out.println("用户 id=" + u.getUserID() + " pwd=" + u.getPasswd() + " 验证失败");
                    message.setMesType(MessageType.MESSAGE_LOGIN_FAIL);
                    oos.writeObject(message);
                    //关闭流，就是不需要接收信息了，
                    socket.close();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //如果服务端退出while循环，说明服务端不再监听了，因此需关闭ServerSocket
            try {
                ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

//    //这个线程是否需要退出一下呢
//    @Override
//    public void run() {
//        while (true) {
//            String news = Utility.readString(100);
//            if ("start".equals(news)) {
//                new Thread(sendNewsToService).start();
//            }
//        }
//    }
}

